package cn.virens.web.components.activiti;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.history.HistoricVariableInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <br>
 * (c) Copyright 2016 by <br>
 * 
 * @description :工作流处理Service <br>
 * @file_name :ActivitiService.java <br>
 * @system_name :ccqlk-security365-activiti <br>
 * @author :cn-loioi <br>
 * @创建时间 :2016年11月17日 上午10:00:59
 */
public class ActivitiService {
	private final Logger logger = LoggerFactory.getLogger(ActivitiService.class);
	private static final boolean DEBUG = true;

	private final ProcessEngine processEngine;

	public ActivitiService(ProcessEngine processEngine) {
		this.processEngine = processEngine;
	}

	private DeploymentBuilder createDeployment() {
		return getRepositoryService().createDeployment();
	}

	/**
	 * 部署流程实例
	 * 
	 * @param bpnmFilePath 流程文件路径
	 * @return
	 * @throws FileNotFoundException
	 */
	public String deploymentBpnmFile(String bpnmFilePath) throws FileNotFoundException {
		return deployment(createDeployment().addInputStream(FilenameUtils.getName(bpnmFilePath), new FileInputStream(bpnmFilePath)).deploy());
	}

	/**
	 * 部署流程实例
	 * 
	 * @param zipInputStream 流程文件路径
	 * @return
	 * @throws FileNotFoundException
	 */
	public String deploymentZipInputStream(ZipInputStream zipInputStream) {
		return deployment(createDeployment().addZipInputStream(zipInputStream).deploy());
	}

	/**
	 * 部署流程实例
	 * 
	 * @param resource 流程文件路径
	 * @return
	 * @throws FileNotFoundException
	 */
	public String deploymentClasspathResource(String resource) {
		return deployment(createDeployment().addClasspathResource(resource).deploy());
	}

	private String deployment(Deployment deployment) {
		ProcessDefinition processDefinition = getRepositoryService()//
				.createProcessDefinitionQuery()//
				.deploymentId(deployment.getId())//
				.singleResult();

		if (DEBUG) logger.debug("流程已部署:processID -> {},processKEY -> {},createTime ->- {}", deployment.getId(), processDefinition.getKey(), deployment.getDeploymentTime());
		return processDefinition.getKey();
	}

	/**
	 * 启动流程
	 * 
	 * @param processDefinitionKey 流程部署KEY
	 * @param businessKey
	 * @param authenticatedUserId activiti:initiator的值
	 * @param variables 流程变量
	 * @return
	 */
	public ProcessInstance startProcessInstanceByKey(String processDefinitionKey, String businessKey, String authenticatedUserId, Map<String, Object> variables) {
		if (StringUtils.isNotEmpty(authenticatedUserId)) getIdentityService().setAuthenticatedUserId(authenticatedUserId);
		ProcessInstance processInstance = null;
		if (StringUtils.isNotEmpty(businessKey) && variables != null) {
			processInstance = getRuntimeService().startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
		} else if (variables != null) {
			processInstance = getRuntimeService().startProcessInstanceByKey(processDefinitionKey, variables);
		} else if (StringUtils.isNotEmpty(businessKey)) {
			processInstance = getRuntimeService().startProcessInstanceByKey(processDefinitionKey, businessKey);
		} else {
			processInstance = getRuntimeService().startProcessInstanceByKey(processDefinitionKey);
		}

		if (DEBUG) logger.debug("流程已启动：processDefinitionId -> {},businessKey -> {},authenticatedUserId -> {},process -> {}", processDefinitionKey, businessKey, authenticatedUserId, processInstance.getId());
		return processInstance;
	}

	/**
	 * 启动流程
	 * 
	 * @param processDefinitionKey 流程部署KEY
	 * @param businessKey
	 * @param variables 流程变量
	 * @return
	 */
	public ProcessInstance startProcessInstanceByKey(String processDefinitionKey, String businessKey, Map<String, Object> variables) {
		return startProcessInstanceByKey(processDefinitionKey, businessKey, null, variables);
	}

	/**
	 * 启动流程
	 * 
	 * @param processDefinitionKey 流程部署KEY
	 * @param variables 流程变量
	 * @return
	 */
	public ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables) {
		return startProcessInstanceByKey(processDefinitionKey, null, null, variables);
	}

	/**
	 * 启动流程
	 * 
	 * @param processDefinitionKey 流程部署KEY
	 * @param businessKey
	 * @return
	 */
	public ProcessInstance startProcessInstanceByKey(String processDefinitionKey, String businessKey) {
		return startProcessInstanceByKey(processDefinitionKey, businessKey, null, null);
	}

	/**
	 * 启动流程
	 * 
	 * @param processDefinitionKey 流程部署KEY
	 * @return
	 */
	public ProcessInstance startProcessInstanceByKey(String processDefinitionKey) {
		return startProcessInstanceByKey(processDefinitionKey, null, null, null);
	}

	/**
	 * 查询Task
	 * 
	 * @return
	 */
	public TaskQuery taskQuery() {
		return getTaskService().createTaskQuery();
	}

	/**
	 * 获取当前任务
	 * 
	 * @param processInstanceId 实例ID
	 * @param candidateUser 可认领人
	 * @param candidateDepartment 可认领部门
	 * @return
	 */
	public Task getTask(String processInstanceId, String candidateUser, String candidateDepartment) {
		TaskQuery query = taskQuery()//
				.processInstanceId(processInstanceId)//
				.or()//
				.taskAssignee(candidateUser)//
				.taskCandidateUser(candidateUser)//
				.taskCandidateGroup(candidateDepartment)//
				.endOr();

		List<Task> list = query.listPage(0, 1);
		if (list.isEmpty()) {
			return null;
		} else {
			return list.get(0);
		}
	}

	/**
	 * 认领任务
	 * 
	 * @param taskId 任务ID
	 * @param userId 认领者
	 */
	public void claimTask(String taskId, String userId) {
		getTaskService().claim(taskId, userId);

		logger.debug("任务({})已被{}认领", taskId, userId);
	}

	/**
	 * 添加 - 任务变量
	 * 
	 * @param taskId
	 * @param variableName
	 * @param value
	 */
	public void setTaskVariable(String taskId, String variableName, Object value) {
		getTaskService().setVariableLocal(taskId, variableName, value);
	}

	/**
	 * 添加 - 任务变量
	 * 
	 * @param taskId
	 * @param variables
	 */
	public void setTaskVariable(String taskId, Map<String, ? extends Object> variables) {
		getTaskService().setVariablesLocal(taskId, variables);
	}

	/**
	 * 完成UTask
	 * 
	 * @param taskId 任务ID
	 * @param proMap 流程变量
	 */
	public void completeTask(String taskId, Map<String, Object> variables) {
		if (variables != null) {
			getTaskService().complete(taskId, variables);
		} else {
			getTaskService().complete(taskId);
		}
		logger.debug("任务({})已完成", taskId);
	}

	/**
	 * 获取流程变量
	 * 
	 * @param processInstanceId 流程实例ID
	 * @return
	 */
	public Map<String, Object> getProcessVariables(String processInstanceId) {
		HistoricVariableInstanceQuery query = getHistoryService()//
				.createHistoricVariableInstanceQuery()//
				.processInstanceId(processInstanceId)//
				.excludeTaskVariables()//
				.orderByVariableName()//
				.asc();

		// 检查是否有流程
		if (query.count() <= 0) return null;
		Map<String, Object> result = new HashMap<>();
		// 获取全部变量
		List<HistoricVariableInstance> list = query.list();
		// 转为map
		for (HistoricVariableInstance a : list) {
			result.put(a.getVariableName(), a.getValue());
		}

		return result;
	}

	/**
	 * 获取流程变量
	 * 
	 * @param processInstanceId 流程实例ID
	 * @param variableNames 要查询的字段
	 * @return
	 */
	public Map<String, Object> getProcessVariables(String processInstanceId, Collection<String> variableNames) {
		HistoricVariableInstanceQuery query = getHistoryService()//
				.createHistoricVariableInstanceQuery()//
				.processInstanceId(processInstanceId)//
				.excludeTaskVariables()//
				.orderByVariableName()//
				.asc();

		// 检查是否有流程
		if (query.count() <= 0) return null;
		Map<String, Object> result = new HashMap<>();
		// 获取全部变量
		List<HistoricVariableInstance> list = query.list();
		// 转为map
		for (String name : variableNames) {
			for (HistoricVariableInstance a : list) {
				if (StringUtils.equals(name, a.getVariableName())) {
					result.put(a.getVariableName(), a.getValue());
				}
			}
		}

		return result;
	}

	/**
	 * 获取任务变量
	 * 
	 * @param taskId 任务ID
	 * @return
	 */
	public Map<String, Object> getTaskVariables(String taskId) {
		HistoricVariableInstanceQuery query = getHistoryService()//
				.createHistoricVariableInstanceQuery()//
				.taskId(taskId)//
				.orderByVariableName()//
				.asc();

		// 检查是否有流程
		if (query.count() <= 0) return null;
		Map<String, Object> result = new HashMap<>();
		// 获取全部变量
		List<HistoricVariableInstance> list = query.list();
		// 转为map
		for (HistoricVariableInstance a : list) {
			result.put(a.getVariableName(), a.getValue());
		}

		return result;
	}

	/**
	 * 获取任务变量
	 * 
	 * @param taskId 任务ID
	 * @param variableNames 要查询的字段
	 * @return
	 */
	public Map<String, Object> getTaskVariables(String taskId, Collection<String> variableNames) {
		HistoricVariableInstanceQuery query = getHistoryService()//
				.createHistoricVariableInstanceQuery()//
				.taskId(taskId)//
				.orderByVariableName()//
				.asc();

		// 检查是否有流程
		if (query.count() <= 0) return null;
		Map<String, Object> result = new HashMap<>();
		// 获取全部变量
		List<HistoricVariableInstance> list = query.list();
		// 转为map
		for (String name : variableNames) {
			for (HistoricVariableInstance a : list) {
				if (StringUtils.equals(name, a.getVariableName())) {
					result.put(a.getVariableName(), a.getValue());
				}
			}
		}

		return result;
	}

	/**
	 * 获取流程历史
	 * 
	 * @param processInstanceId 流程实例ID
	 * @return
	 */
	public List<Map<String, Object>> getProcessTasks(String processInstanceId) {
		HistoricTaskInstanceQuery query = getHistoryService()//
				.createHistoricTaskInstanceQuery()//
				.processInstanceId(processInstanceId)//
				.includeTaskLocalVariables()//
				.orderByTaskCreateTime()//
				.asc();

		// 检查是否有流程
		if (query.count() <= 0) return null;
		List<Map<String, Object>> result = new ArrayList<>();

		List<HistoricTaskInstance> list = query.list();

		for (HistoricTaskInstance task : list) {
			Map<String, Object> map = new HashMap<>();
			map.put("assignee", task.getAssignee());

			map.put("createTime", task.getCreateTime());
			map.put("startTime", task.getStartTime());
			map.put("claimTime", task.getClaimTime());
			map.put("endTime", task.getEndTime());

			map.put("state", task.getDeleteReason());
			// 获取任务变量
			map.put("variables", task.getTaskLocalVariables());

			result.add(map);
		}

		return result;
	}

	public ProcessEngine getProcessEngine() {
		return processEngine;
	}

	public TaskService getTaskService() {
		return processEngine.getTaskService();
	}

	public HistoryService getHistoryService() {
		return processEngine.getHistoryService();
	}

	public RuntimeService getRuntimeService() {
		return processEngine.getRuntimeService();
	}

	public IdentityService getIdentityService() {
		return processEngine.getIdentityService();
	}

	public RepositoryService getRepositoryService() {
		return processEngine.getRepositoryService();
	}

	public ManagementService getManagementService() {
		return processEngine.getManagementService();
	}

}
